//
//  NNCaramaController.swift
//  CommonTool
//
//  Created by tenroadshow on 1.3.22.
//

import UIKit
import AVFoundation
import Photos


/*

 builtInMicrophone         内置麦克风。
 builtInWideAngleCamera    内置广角相机。
 builtInTelephotoCamera    内置摄像头设备的焦距比广角摄像头更长。
 builtInUltraWideCamera    内置相机的焦距比广角相机的焦距短。
 builtInDualCamera         广角相机和远摄相机的组合
 builtInDualWideCamera     一种设备，包括两个固定焦距的相机，一个超广角和一个广角
 builtInTripleCamera       一种设备，该设备由三个固定焦距的相机，一个超广角，一个广角和一个长焦相机组成。
 builtInTrueDepthCamera    相机和其他传感器的组合，可创建能够进行照片，视频和深度捕捉的捕捉设备。
 builtInDuoCamera    iOS 10.2 之后不推荐使用
 
 **/


private enum SessionSetupResult {
    case success
    case notAuthorized
    case configurationFailed
}


@available(iOS 13, *)
class NNCaramaController: UIViewController {

    // 设备方向
    var windowOrientation: UIInterfaceOrientation {
        return view.window?.windowScene?.interfaceOrientation ?? .unknown
    }

    
    lazy var context = CIContext()
    
    // 照片元数据
    private var photoData: Data?
    // 照片元数据
    private var portraitEffectsMatteData: Data?
    // Live照片元数据
    private var semanticSegmentationMatteDataArray: [Data] = []
    
    
    private var inProgressPhotoCaptureDelegates = [Int64: NNPhotoCaptureProcessor]()
    
    private var inProgressLivePhotoCapturesCount = 0
        
    // 初始化结果
    private var setupResult: SessionSetupResult = .notAuthorized
    
    private let videoDeviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera, .builtInDualCamera, .builtInTrueDepthCamera],
                                                                               mediaType: .video, position: .unspecified)
    // 是否打开闪光灯
    public var flashMode: AVCaptureDevice.FlashMode = .off
    
    // 是否支持人像捕捉
    public var isSupportPortraitEffectsMatteDelivery: Bool = false
    
    // UI
    private var spinner: UIActivityIndicatorView!
    
    lazy var contentView: UIView = {
        let contentView = UIView()
        contentView.layer.insertSublayer(preViewLayer, at: 0)
        return contentView
    }()
    
    
    lazy var preViewLayer: AVCaptureVideoPreviewLayer = {
        let preViewLayer = AVCaptureVideoPreviewLayer(session: self.session)
        preViewLayer.videoGravity = .resizeAspectFill
        return preViewLayer
    }()

    lazy var takePhotoBtn: UIButton = {
        let btn = UIButton(type: .custom)
        btn.setImage(UIImage(named: "CapturePhoto")?.withRenderingMode(.alwaysOriginal), for: .normal)
        btn.addTarget(self, action: #selector(takePhoto), for: .touchUpInside)
        return btn
    }()
    
    lazy var toggleBtn: UIButton = {
        let btn = UIButton(type: .custom)
        btn.setImage(UIImage(named: "FlipCamera")?.withRenderingMode(.alwaysOriginal), for: .normal)
        btn.addTarget(self, action: #selector(toggleFrontOrBack), for: .touchUpInside)
        return btn
    }()
    
    lazy var flashBtn: UIButton = {
        let btn = UIButton(type: .custom)
        btn.backgroundColor = .random
        btn.addTarget(self, action: #selector(changeFlashMode), for: .touchUpInside)
        return btn
    }()


    lazy var focusView: UIView = {
        let focusView = UIView(frame: CGRect(origin: .zero, size: CGSize(width: 50, height: 50)))
        focusView.layer.borderWidth = 1.0
        focusView.layer.borderColor = UIColor.green.cgColor
        focusView.layer.backgroundColor = UIColor.clear.cgColor
        focusView.isHidden = true
        return focusView
    }()
    
    lazy var bottomContainer: UIView = {
        let view = UIView()
        view.backgroundColor = .random
        return view
    }()
    
    
    lazy var ablumBtn: UIButton = {
        let btn = UIButton(type: .custom)
        btn.backgroundColor = .random
        btn.addTarget(self,
                      action: #selector(showAblum),
                      for: .touchUpInside)
        return btn
    }()
    
    
    
    
    
    private var videoInput: AVCaptureDeviceInput!
    private var photoOutput = AVCapturePhotoOutput()
    private var sessionQueue = DispatchQueue(label: "sessionQueue")
    private let session = AVCaptureSession()
    
    // 展示
    public static func show() {
        let controller = NNCaramaController()
        controller.modalPresentationStyle = .fullScreen
        UIViewController.topViewController(nil)?.present(controller, animated: true, completion: nil)
    }
    
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        preViewLayer.frame = contentView.bounds
    }
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        // 布局UI
        // 添加对焦手势
        contentView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(focusAction(tap:))))

        
        contentView.addSubviews(childs: [focusView, toggleBtn, flashBtn])
        bottomContainer.addSubview(takePhotoBtn)
        bottomContainer.addSubviews(childs: [takePhotoBtn, ablumBtn])
        view.addSubviews(childs: [contentView, bottomContainer])
        
        
        contentView.snp.makeConstraints { make in
            make.edges.equalToSuperview().inset(UIEdgeInsets(top: 0, left: 0, bottom: 200, right: 0))
        }
        
        toggleBtn.snp.makeConstraints { make in
            make.top.equalToSuperview().offset(50)
            make.right.equalToSuperview().offset(-50)
            make.size.equalTo(CGSize(width: 50, height: 50))
        }
        flashBtn.snp.makeConstraints { make in
            make.top.left.equalToSuperview().offset(50)
            make.size.equalTo(CGSize(width: 50, height: 50))
        }
                
        bottomContainer.snp.makeConstraints { make in
            make.top.equalTo(contentView.snp.bottom)
            make.left.bottom.right.equalToSuperview()
        }
        takePhotoBtn.snp.makeConstraints { make in
            make.center.equalToSuperview()
            make.size.equalTo(CGSize(width: 60, height: 60))
        }
        ablumBtn.snp.makeConstraints { make in
            make.centerY.equalToSuperview()
            make.left.equalToSuperview().offset(20)
            make.size.equalTo(CGSize(width: 100, height: 100))
        }

        
        
        // 1. 获取设备权限
        switch AVCaptureDevice.authorizationStatus(for: .video) {
        case .authorized:
            //用户授予应用访问捕获设备的权限
            setupResult = .success
        case .notDetermined:
            //不允许用户访问媒体捕获设备。这个状态通常是看不到的：用于发现设备的`AVCaptureDevice`类方法不会返回用户被限制访问的设备
            sessionQueue.suspend()
            AVCaptureDevice.requestAccess(for: .video) { granted in
                if granted {
                    self.setupResult = .success
                }else {
                    self.setupResult = .notAuthorized
                }
                self.sessionQueue.resume()
            }
        default:
            setupResult = .notAuthorized
        }
        
        sessionQueue.async {
            self.configSession()
        }
        
        DispatchQueue.main.async {
            self.spinner = UIActivityIndicatorView(style: .large)
            self.spinner.color = UIColor.white
            self.contentView.addSubview(self.spinner)
        }
    }
    
    func configSession() {
        // 如果没有权限，则没必要执行下一步
        if setupResult != .success { return }
        
        
        session.beginConfiguration()
        session.sessionPreset = .photo
        do {
            var defaultVideoDevice: AVCaptureDevice?
            
            if let backCameraDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) {
                // builtInWideAngleCamera: 内置广角相机， 后置
                defaultVideoDevice = backCameraDevice
            } else  if let dualCameraDevice = AVCaptureDevice.default(.builtInDualCamera, for: .video, position: .back) {
                // builtInDualCamera: 广角相机和远摄相机的组合、后置
                defaultVideoDevice = dualCameraDevice

            } else if let frontCameraDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front) {
                // builtInWideAngleCamera: 内置广角相机， 前置
                defaultVideoDevice = frontCameraDevice
            }
            guard let videoDevice = defaultVideoDevice else {
                CC.log("Default video device is unavailable.")
                setupResult = .configurationFailed
                session.commitConfiguration()
                return
            }
            // 添加输入
            let input = try AVCaptureDeviceInput(device: videoDevice)
            if session.canAddInput(input) {
                session.addInput(input)
                videoInput = input
            }else {
                setupResult = .configurationFailed
                session.commitConfiguration()
                return
            }

            // 添加输出
            if session.canAddOutput(photoOutput) {
                session.addOutput(photoOutput)
                
                // 是否启用高分辨率捕获
                photoOutput.isHighResolutionCaptureEnabled = true
                photoOutput.isLivePhotoCaptureEnabled = photoOutput.isLivePhotoCaptureSupported
                photoOutput.isDepthDataDeliveryEnabled = photoOutput.isDepthDataDeliverySupported
                photoOutput.isPortraitEffectsMatteDeliveryEnabled = photoOutput.isPortraitEffectsMatteDeliverySupported
                photoOutput.enabledSemanticSegmentationMatteTypes = photoOutput.availableSemanticSegmentationMatteTypes
                photoOutput.maxPhotoQualityPrioritization = .quality
            }else {
                setupResult = .configurationFailed
                session.commitConfiguration()
                return
            }
        } catch {
            setupResult = .configurationFailed
            session.commitConfiguration()
            return
        }
        session.commitConfiguration()
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        sessionQueue.async {
            switch self.setupResult {
            case .success:
                self.session.startRunning()
            case .notAuthorized:
                DispatchQueue.main.async {
                    let alertController = UIAlertController(title: "提示", message: "没有相机权限，请打开设置", preferredStyle: .alert)
                    alertController.addAction(UIAlertAction(title: "OK",
                                                            style: .cancel,
                                                            handler: nil))
                    
                    alertController.addAction(UIAlertAction(title: "Settings",
                                                            style: .default,
                                                            handler: { _ in
                                                                UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!,
                                                                                          options: [:],
                                                                                          completionHandler: nil)
                    }))
                    self.present(alertController, animated: true, completion: nil)
                }
            case .configurationFailed:
                DispatchQueue.main.async {
                    let alertController = UIAlertController(title: "提示", message: "相机配置出错", preferredStyle: .alert)
                    
                    alertController.addAction(UIAlertAction(title: "OK",
                                                            style: .cancel,
                                                            handler: nil))
                    self.present(alertController, animated: true, completion: nil)
                }
            }
        }
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        sessionQueue.async {
            if self.setupResult == .success {
                self.session.stopRunning()
            }
        }
        super.viewWillDisappear(animated)
    }
    
    
    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return .portrait
    }
}



// MARK: - 事件
@available(iOS 13, *)
extension NNCaramaController {
    
    @objc func showAblum() {
        CC.log("显示相册")
    }
    
    
    // 事件：聚焦
    @objc func focusAction(tap: UITapGestureRecognizer) {
        let point = tap.location(in: tap.view)
        focus(point: point)
    }
    
    // 事件：聚焦
    private func focus(point: CGPoint) {
        let size = contentView.bounds.size
        
        let focusPoint = CGPoint(x: point.y / size.height,
                                 y: 1.0 - point.x / size.width)
        do {
            try self.videoInput.device.lockForConfiguration()
            
            if self.videoInput.device.isFocusModeSupported(.autoFocus) {
                videoInput.device.focusPointOfInterest =  focusPoint
                videoInput.device.focusMode = .autoFocus
            }
            
            if videoInput.device.isExposureModeSupported(.autoExpose) {
                videoInput.device.exposurePointOfInterest = focusPoint
                videoInput.device.exposureMode = .autoExpose
            }
            videoInput.device.unlockForConfiguration()
            
            focusView.center = point
            focusView.isHidden = false
            UIView.animate(withDuration: 0.35) {
                self.focusView.transform = CGAffineTransform.init(scaleX: 1.25, y: 1.25)
            } completion: { _ in
                UIView.animate(withDuration: 0.5,
                               animations: {
                    self.focusView.transform = .identity
                }, completion: { _ in
                    self.focusView.isHidden = true
                })
            }
        } catch {
            CC.log("focus error: \(error)")
        }
    }
    
    // 拍照
    @objc func takePhoto() {
        sessionQueue.async {
            if let videoPreviewLayerOrientation = self.preViewLayer.connection?.videoOrientation,
                let photoOutputConnection = self.photoOutput.connection(with: .video) {
                photoOutputConnection.videoOrientation = videoPreviewLayerOrientation
            }

            var settings = AVCapturePhotoSettings()
            // Capture HEIF photos when supported. Enable auto-flash and high-resolution photos.
            if  self.photoOutput.availablePhotoCodecTypes.contains(.hevc) {
                settings = AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.hevc])
            }
            
            if self.videoInput.device.isFlashAvailable {
                settings.flashMode = .auto
            }
            
            settings.isHighResolutionPhotoEnabled = true
            if !settings.__availablePreviewPhotoPixelFormatTypes.isEmpty {
                settings.previewPhotoFormat = [kCVPixelBufferPixelFormatTypeKey as String: settings.__availablePreviewPhotoPixelFormatTypes.first!]
            }
            
            // 设置闪光灯模式
            settings.flashMode = self.flashMode
            // 是否支持人像捕捉
            settings.isPortraitEffectsMatteDeliveryEnabled = (self.isSupportPortraitEffectsMatteDelivery == true
                    && self.photoOutput.isPortraitEffectsMatteDeliveryEnabled)
            settings.photoQualityPrioritization = .quality

            let photoCaptureProcessor = NNPhotoCaptureProcessor(with: settings) {
                DispatchQueue.main.async {
                    self.preViewLayer.opacity = 0
                    UIView.animate(withDuration: 0.25) {
                        self.preViewLayer.opacity = 1
                    }
                }
            } completionHandler: { photoCaptureProcessor in
                self.sessionQueue.async {
                    self.inProgressPhotoCaptureDelegates[photoCaptureProcessor.requestedPhotoSettings.uniqueID] = nil
                }
            } photoProcessingHandler: { animate in
                // Animates a spinner while photo is processing
                DispatchQueue.main.async {
                    if animate {
                        self.spinner.hidesWhenStopped = true
                        self.spinner.center = CGPoint(x: self.contentView.frame.size.width / 2.0, y: self.contentView.frame.size.height / 2.0)
                        self.spinner.startAnimating()
                    } else {
                        self.spinner.stopAnimating()
                    }
                }
            }
            self.inProgressPhotoCaptureDelegates[photoCaptureProcessor.requestedPhotoSettings.uniqueID] = photoCaptureProcessor
            self.photoOutput.capturePhoto(with: settings, delegate: photoCaptureProcessor)
        }
    }
    
    @objc func changeFlashMode() {
        switch self.flashMode {
        case .auto:
            flashMode = .off
            flashBtn.setTitle("off", for: .normal)
        case .on:
            flashMode = .auto
            flashBtn.setTitle("auto", for: .normal)
        case .off:
            flashMode = .on
            flashBtn.setTitle("on", for: .normal)
        default:
            flashMode = .off
            flashBtn.setTitle("off", for: .normal)
        }
    }
    
    @objc func toggleFrontOrBack() {
        sessionQueue.async {
            let currentVideoDevice = self.videoInput.device
            let currentPosition = currentVideoDevice.position
            
            let preferredPosition: AVCaptureDevice.Position
            let preferredDeviceType: AVCaptureDevice.DeviceType
            
            switch currentPosition {
            case .unspecified, .front:
                preferredPosition = .back
                // 广角相机和远摄相机的组合
                preferredDeviceType = .builtInDualCamera
                
            case .back:
                preferredPosition = .front
                // 相机和其他传感器的组合，可创建能够进行照片，视频和深度捕捉的捕捉设备。
                preferredDeviceType = .builtInTrueDepthCamera
                
            @unknown default:
                CC.log("Unknown capture position. Defaulting to back, dual-camera.")
                preferredPosition = .back
                // 广角相机和远摄相机的组合
                preferredDeviceType = .builtInDualCamera
            }
            let devices = self.videoDeviceDiscoverySession.devices
            var newVideoDevice: AVCaptureDevice? = nil
            
            // First, seek a device with both the preferred position and device type. Otherwise, seek a device with only the preferred position.
            if let device = devices.first(where: { $0.position == preferredPosition && $0.deviceType == preferredDeviceType }) {
                newVideoDevice = device
            } else if let device = devices.first(where: { $0.position == preferredPosition }) {
                newVideoDevice = device
            }
            
            if let videoDevice = newVideoDevice {
                do {
                    let videoDeviceInput = try AVCaptureDeviceInput(device: videoDevice)
                    
                    self.session.beginConfiguration()
                    // 首先移除已存在的输入，因为AVCaptureSession不支持多个输入
                    self.session.removeInput(self.videoInput)
                    
                    if self.session.canAddInput(videoDeviceInput) {
                        self.session.addInput(videoDeviceInput)
                        self.videoInput = videoDeviceInput
                    } else {
                        self.session.addInput(self.videoInput)
                    }
                    self.photoOutput.isDepthDataDeliveryEnabled = self.photoOutput.isDepthDataDeliverySupported
                    self.photoOutput.isPortraitEffectsMatteDeliveryEnabled = self.photoOutput.isPortraitEffectsMatteDeliverySupported
                    self.photoOutput.enabledSemanticSegmentationMatteTypes = self.photoOutput.availableSemanticSegmentationMatteTypes
                    self.photoOutput.maxPhotoQualityPrioritization = .quality
                    self.session.commitConfiguration()
                } catch {
                    CC.log("Error occurred while creating video device input: \(error)")
                }
            }
        }
    }
}
